"""依赖包管理器
负责Python包的安装、卸载、版本管理等功能
"""
import subprocess
import sys
import os
import json
import pkg_resources
from typing import List, Dict, Optional, Tuple
import tempfile
import time
from packaging import version
import requests
import traceback

# 导入日志工具
from services.shared.log_utils import log_error, log_info, log_system_event
from services.shared.logger import get_logger

# 获取日志记录器
logger = get_logger('executor.dependency_manager')


class DependencyManager:
    """依赖包管理器"""
    
    def __init__(self):
        self.pip_executable = sys.executable
        self.cache_dir = os.path.join(tempfile.gettempdir(), 'xbyjob_pip_cache')
        self.ensure_cache_dir()
    
    def ensure_cache_dir(self):
        """确保缓存目录存在"""
        if not os.path.exists(self.cache_dir):
            os.makedirs(self.cache_dir, exist_ok=True)
    
    def install_package(self, package_spec: str, timeout: int = 300, 
                       index_url: Optional[str] = None, trusted_host: bool = False) -> Tuple[bool, str]:
        """
        安装Python包
        
        Args:
            package_spec: 包规格，如 'requests==2.25.1' 或 'numpy>=1.20.0'
            timeout: 超时时间（秒）
            index_url: 镜像源URL
            trusted_host: 是否信任镜像源主机
            
        Returns:
            (是否成功, 消息)
        """
        try:
            cmd = [
                self.pip_executable, '-m', 'pip', 'install',
                '--cache-dir', self.cache_dir,
                '--timeout', str(timeout),
            ]
            
            # 如果指定了镜像源，添加相关参数
            if index_url:
                cmd.extend(['-i', index_url])
                
                # 如果需要信任主机，添加 --trusted-host 参数
                if trusted_host:
                    # 从URL中提取主机名
                    from urllib.parse import urlparse
                    parsed_url = urlparse(index_url)
                    if parsed_url.hostname:
                        cmd.extend(['--trusted-host', parsed_url.hostname])
            
            cmd.append(package_spec)
            
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=timeout,
                check=True
            )
            
            # 安装成功后刷新pkg_resources.working_set
            pkg_resources.working_set = pkg_resources.WorkingSet()
            
            # 记录成功安装的信息日志
            log_info(
                module='dependency_manager',
                message=f"包安装成功: {package_spec}",
                details=json.dumps({
                    'package_spec': package_spec,
                    'timeout': timeout,
                    'index_url': index_url,
                    'stdout': result.stdout
                }, ensure_ascii=False)
            )
            return True, f"成功安装包: {package_spec}\n{result.stdout}"
            
        except subprocess.CalledProcessError as e:
            error_msg = f"安装包失败: {package_spec}\n错误: {e.stderr}"
            log_error(
                module='dependency_manager',
                message=f"包安装失败: {package_spec}",
                details=json.dumps({
                    'package_spec': package_spec,
                    'error_type': 'CalledProcessError',
                    'return_code': e.returncode,
                    'stderr': e.stderr,
                    'stdout': e.stdout,
                    'cmd': ' '.join(cmd),
                    'timeout': timeout,
                    'index_url': index_url
                }, ensure_ascii=False)
            )
            return False, error_msg
        except subprocess.TimeoutExpired:
            error_msg = f"安装包超时: {package_spec}"
            log_error(
                module='dependency_manager',
                message=f"包安装超时: {package_spec}",
                details=json.dumps({
                    'package_spec': package_spec,
                    'error_type': 'TimeoutExpired',
                    'timeout': timeout,
                    'cmd': ' '.join(cmd),
                    'index_url': index_url
                }, ensure_ascii=False)
            )
            return False, error_msg
        except Exception as e:
            error_msg = f"安装包异常: {str(e)}"
            log_error(
                module='dependency_manager',
                message=f"包安装异常: {package_spec}",
                details=json.dumps({
                    'package_spec': package_spec,
                    'error_type': type(e).__name__,
                    'error_message': str(e),
                    'traceback': traceback.format_exc(),
                    'timeout': timeout,
                    'index_url': index_url
                }, ensure_ascii=False)
            )
            return False, error_msg
    
    def uninstall_package(self, package_name: str) -> Tuple[bool, str]:
        """
        卸载Python包
        
        Args:
            package_name: 包名
            
        Returns:
            (是否成功, 消息)
        """
        try:
            cmd = [
                self.pip_executable, '-m', 'pip', 'uninstall',
                '-y',  # 自动确认
                package_name
            ]
            
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=True
            )
            
            # 卸载成功后刷新pkg_resources.working_set
            pkg_resources.working_set = pkg_resources.WorkingSet()
            
            # 记录成功卸载的信息日志
            log_info(
                module='dependency_manager',
                message=f"包卸载成功: {package_name}",
                details=json.dumps({
                    'package_name': package_name,
                    'stdout': result.stdout
                }, ensure_ascii=False)
            )
            return True, f"成功卸载包: {package_name}\n{result.stdout}"
            
        except subprocess.CalledProcessError as e:
            error_msg = f"卸载包失败: {package_name}\n错误: {e.stderr}"
            log_error(
                module='dependency_manager',
                message=f"包卸载失败: {package_name}",
                details=json.dumps({
                    'package_name': package_name,
                    'error_type': 'CalledProcessError',
                    'return_code': e.returncode,
                    'stderr': e.stderr,
                    'stdout': e.stdout,
                    'cmd': ' '.join(cmd)
                }, ensure_ascii=False)
            )
            return False, error_msg
        except Exception as e:
            error_msg = f"卸载包异常: {str(e)}"
            log_error(
                module='dependency_manager',
                message=f"包卸载异常: {package_name}",
                details=json.dumps({
                    'package_name': package_name,
                    'error_type': type(e).__name__,
                    'error_message': str(e),
                    'traceback': traceback.format_exc()
                }, ensure_ascii=False)
            )
            return False, error_msg
    
    def upgrade_package(self, package_name: str, timeout: int = 300) -> Tuple[bool, str]:
        """
        升级Python包
        
        Args:
            package_name: 包名
            timeout: 超时时间（秒）
            
        Returns:
            (是否成功, 消息)
        """
        try:
            cmd = [
                self.pip_executable, '-m', 'pip', 'install',
                '--upgrade',
                '--cache-dir', self.cache_dir,
                '--timeout', str(timeout),
                package_name
            ]
            
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=timeout,
                check=True
            )
            
            # 升级成功后刷新pkg_resources.working_set
            pkg_resources.working_set = pkg_resources.WorkingSet()
            
            # 记录成功升级的信息日志
            log_info(
                module='dependency_manager',
                message=f"包升级成功: {package_name}",
                details=json.dumps({
                    'package_name': package_name,
                    'timeout': timeout,
                    'stdout': result.stdout
                }, ensure_ascii=False)
            )
            return True, f"成功升级包: {package_name}\n{result.stdout}"
            
        except subprocess.CalledProcessError as e:
            error_msg = f"升级包失败: {package_name}\n错误: {e.stderr}"
            log_error(
                module='dependency_manager',
                message=f"包升级失败: {package_name}",
                details=json.dumps({
                    'package_name': package_name,
                    'error_type': 'CalledProcessError',
                    'return_code': e.returncode,
                    'stderr': e.stderr,
                    'stdout': e.stdout,
                    'cmd': ' '.join(cmd),
                    'timeout': timeout
                }, ensure_ascii=False)
            )
            return False, error_msg
        except subprocess.TimeoutExpired:
            error_msg = f"升级包超时: {package_name}"
            log_error(
                module='dependency_manager',
                message=f"包升级超时: {package_name}",
                details=json.dumps({
                    'package_name': package_name,
                    'error_type': 'TimeoutExpired',
                    'timeout': timeout,
                    'cmd': ' '.join(cmd)
                }, ensure_ascii=False)
            )
            return False, error_msg
        except Exception as e:
            error_msg = f"升级包异常: {str(e)}"
            log_error(
                module='dependency_manager',
                message=f"包升级异常: {package_name}",
                details=json.dumps({
                    'package_name': package_name,
                    'error_type': type(e).__name__,
                    'error_message': str(e),
                    'traceback': traceback.format_exc(),
                    'timeout': timeout
                }, ensure_ascii=False)
            )
            return False, error_msg
    
    def install_requirements(self, requirements: List[str], timeout: int = 600) -> List[Dict]:
        """
        批量安装依赖包
        
        Args:
            requirements: 依赖包列表
            timeout: 总超时时间（秒）
            
        Returns:
            安装结果列表
        """
        results = []
        start_time = time.time()
        
        for req in requirements:
            if time.time() - start_time > timeout:
                results.append({
                    'package': req,
                    'success': False,
                    'message': '批量安装总超时'
                })
                continue
            
            remaining_time = timeout - (time.time() - start_time)
            package_timeout = min(300, remaining_time)  # 单个包最多5分钟
            
            success, message = self.install_package(req, int(package_timeout))
            results.append({
                'package': req,
                'success': success,
                'message': message
            })
            
            if not success:
                log_error(
                    module='dependency_manager',
                    message=f"批量安装中包安装失败: {req}",
                    details=json.dumps({
                        'package': req,
                        'error_message': message
                    }, ensure_ascii=False)
                )
        
        return results
    
    def get_installed_packages(self) -> List[Dict]:
        """获取已安装的包列表"""
        try:
            logger.debug("dependency_manager.get_installed_packages 开始执行")
            
            # 简化版本：直接使用subprocess获取包列表，避免pkg_resources的问题
            import subprocess
            import json
            
            try:
                # 使用pip list --format=json获取包列表
                result = subprocess.run(
                    [self.pip_executable, '-m', 'pip', 'list', '--format=json'],
                    capture_output=True,
                    text=True,
                    timeout=60,  # 60秒超时
                    check=True
                )
                
                packages_data = json.loads(result.stdout)
                logger.debug(f"pip list返回了 {len(packages_data)} 个包")
                
                # 转换为我们需要的格式
                installed_packages = []
                for pkg in packages_data:
                    package_info = {
                        'name': pkg['name'],
                        'version': pkg['version'],
                        'location': '',  # pip list不提供位置信息
                        'summary': ''  # 暂时不获取summary以提高性能
                    }
                    installed_packages.append(package_info)
                
                logger.debug("包列表转换完成")
                return sorted(installed_packages, key=lambda x: x['name'].lower())
                
            except subprocess.TimeoutExpired:
                logger.warning("pip list超时")
                return []
            except subprocess.CalledProcessError as e:
                logger.error(f"pip list失败: {e}")
                return []
            except json.JSONDecodeError as e:
                logger.error(f"解析pip list输出失败: {e}")
                return []
            
        except Exception as e:
            log_error(
                module='dependency_manager',
                message="获取已安装包列表失败",
                details=json.dumps({
                    'error_type': type(e).__name__,
                    'error_message': str(e),
                    'traceback': traceback.format_exc()
                }, ensure_ascii=False)
            )
            return []
    
    def get_package_info(self, package_name: str) -> Optional[Dict]:
        """获取包信息"""
        try:
            # 检查是否已安装
            try:
                dist = pkg_resources.get_distribution(package_name)
                installed_version = dist.version
                installed = True
            except pkg_resources.DistributionNotFound:
                installed_version = None
                installed = False
            
            # 从PyPI获取最新版本信息
            try:
                response = requests.get(f"https://pypi.org/pypi/{package_name}/json", timeout=10)
                if response.status_code == 200:
                    pypi_data = response.json()
                    latest_version = pypi_data['info']['version']
                    summary = pypi_data['info']['summary']
                    author = pypi_data['info']['author']
                    home_page = pypi_data['info']['home_page']
                else:
                    latest_version = None
                    summary = None
                    author = None
                    home_page = None
            except Exception:
                latest_version = None
                summary = None
                author = None
                home_page = None
            
            return {
                'name': package_name,
                'installed': installed,
                'installed_version': installed_version,
                'latest_version': latest_version,
                'summary': summary,
                'author': author,
                'home_page': home_page,
                'update_available': (
                    installed and latest_version and 
                    version.parse(installed_version) < version.parse(latest_version)
                ) if installed_version and latest_version else False
            }
            
        except Exception as e:
            log_error(
                module='dependency_manager',
                message=f"获取包信息失败: {package_name}",
                details=json.dumps({
                    'package_name': package_name,
                    'error_type': type(e).__name__,
                    'error_message': str(e),
                    'traceback': traceback.format_exc()
                }, ensure_ascii=False)
            )
            return None
    
    def search_packages(self, query: str, limit: int = 20) -> List[Dict]:
        """搜索包（使用PyPI搜索API的替代方案）"""
        try:
            # 由于PyPI搜索API经常不可用，这里使用简单的名称匹配
            # 在实际应用中，可以考虑使用其他搜索服务
            
            # 获取一些常用包的列表进行匹配
            common_packages = [
                'requests', 'numpy', 'pandas', 'flask', 'django', 'fastapi',
                'sqlalchemy', 'celery', 'redis', 'beautifulsoup4', 'scrapy',
                'pytest', 'black', 'flake8', 'pillow', 'matplotlib', 'seaborn',
                'scikit-learn', 'tensorflow', 'torch', 'opencv-python'
            ]
            
            results = []
            query_lower = query.lower()
            
            for package in common_packages:
                if query_lower in package.lower():
                    info = self.get_package_info(package)
                    if info:
                        results.append(info)
                    
                    if len(results) >= limit:
                        break
            
            return results
            
        except Exception as e:
            log_error(
                module='dependency_manager',
                message="搜索包失败",
                details=json.dumps({
                    'query': query,
                    'limit': limit,
                    'error_type': type(e).__name__,
                    'error_message': str(e),
                    'traceback': traceback.format_exc()
                }, ensure_ascii=False)
            )
            return []
    
    def validate_requirements(self, requirements: List[str]) -> List[Dict]:
        """验证依赖包规格"""
        results = []
        
        for req in requirements:
            try:
                # 使用pkg_resources解析依赖规格
                parsed = pkg_resources.Requirement.parse(req)
                
                result = {
                    'requirement': req,
                    'valid': True,
                    'package_name': parsed.project_name,
                    'specs': str(parsed.specs) if parsed.specs else 'any',
                    'extras': list(parsed.extras) if parsed.extras else []
                }
                
                # 检查包是否存在于PyPI
                info = self.get_package_info(parsed.project_name)
                if info:
                    result['exists'] = True
                    result['latest_version'] = info['latest_version']
                else:
                    result['exists'] = False
                    result['latest_version'] = None
                
            except Exception as e:
                result = {
                    'requirement': req,
                    'valid': False,
                    'error': str(e)
                }
            
            results.append(result)
        
        return results
    
    def generate_requirements_txt(self) -> str:
        """生成requirements.txt内容"""
        try:
            packages = self.get_installed_packages()
            
            lines = []
            for package in packages:
                # 跳过一些系统包
                if package['name'].lower() in ['pip', 'setuptools', 'wheel']:
                    continue
                
                lines.append(f"{package['name']}=={package['version']}")
            
            return '\n'.join(sorted(lines))
            
        except Exception as e:
            error_msg = f"# 生成requirements.txt失败: {str(e)}"
            log_error(
                module='dependency_manager',
                message="生成requirements.txt失败",
                details=json.dumps({
                    'error_type': type(e).__name__,
                    'error_message': str(e),
                    'traceback': traceback.format_exc()
                }, ensure_ascii=False)
            )
            return error_msg
    
    def clean_cache(self) -> Tuple[bool, str]:
        """清理pip缓存"""
        try:
            import shutil
            
            if os.path.exists(self.cache_dir):
                shutil.rmtree(self.cache_dir)
                self.ensure_cache_dir()
            
            # 同时清理pip内置缓存
            cmd = [self.pip_executable, '-m', 'pip', 'cache', 'purge']
            
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True
            )
            
            return True, f"缓存清理完成\n{result.stdout}"
            
        except Exception as e:
            error_msg = f"清理缓存失败: {str(e)}"
            log_error(
                module='dependency_manager',
                message="清理pip缓存失败",
                details=json.dumps({
                    'cache_dir': self.cache_dir,
                    'error_type': type(e).__name__,
                    'error_message': str(e),
                    'traceback': traceback.format_exc()
                }, ensure_ascii=False)
            )
            return False, error_msg


# 全局依赖管理器实例
dependency_manager = DependencyManager()